package com.user.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.user.config.AlipayConfig;
import com.user.dao.dish.DishIdAndName;
import com.user.dao.order.*;
import com.user.dao.user.RestaurantOrder;
import com.user.domain.Order;
import com.user.domain.User;
import com.user.domain.UserCoupon;
import com.user.mapper.*;
import com.user.service.OrderService;
import com.user.utils.R;
import com.user.utils.WebSocketServer;
import lombok.var;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * @author YU
 * @Description 针对表【order】的数据库操作Service实现
 * @Date 2024-01-08 11:33:40
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private final ObjectMapper objectMapper = new ObjectMapper();
    @Resource
    UserMapper userMapper;
    @Resource
    OrderMapper orderMapper;
    @Resource
    DishMapper dishMapper;
    @Resource
    RestaurantMapper restaurantMapper;
    @Resource
    WebSocketServer webSocketServer;
    @Resource
    MenberMapper menberMapper;
    @Resource
    UserCouponMapper userCouponMapper;

    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    public void cancelUnpaidOrders() {
        List<SchedOrder> unpaidOrders = orderMapper.selectUnpaidOrders();
        Order order1 = new Order();
        LocalDateTime now = LocalDateTime.now();
        for (SchedOrder order : unpaidOrders) {
            LocalDateTime orderTime = order.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            long minutesBetween = ChronoUnit.MINUTES.between(orderTime, now);
            if (minutesBetween >= 30) {
                order1.setId(order.getId());
                order1.setStatus(2);
                orderMapper.updateById(order1);
            }
        }
    }

    @Override
    public R saveOrder(OrderDao order) throws IOException {
        if (order == null) return R.error("订单信息为空");
        LocalDateTime now = LocalDateTime.now().withHour(8).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime orderDate = order.getDatePeriod().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String orderDateString = orderDate.format(formatter);

        //判断时间
        long daysBetween = ChronoUnit.DAYS.between(now, orderDate);
        if (daysBetween > 7) return R.error("预定时间不能超过7天");
        if (daysBetween < 0) return R.error("预定时间不能是过去的时间");

        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        Object loginId = tokenInfo.getLoginId();
        Long loginIdLong = Long.parseLong(loginId.toString());
        Integer id = userMapper.selectIdByPhone(loginIdLong);
        if (!Objects.equals(id, order.getUserId())) return R.error("用户信息错误");
        order.setStatus(0);
        //判断座位
        Integer seatTimeBy = orderMapper.getSeatTimeBy(order.getSeatId(), orderDateString, order.getTimePeriod());
        if (seatTimeBy != 0) return R.error("座位已被预定");
        Map<Integer, Integer> orderDetails = objectMapper.readValue(order.getOrderDetail(), new TypeReference<Map<Integer, Integer>>() {
        });
        User user = userMapper.selectById(order.getUserId());
        //查询会员折扣
        Integer memberId = user.getMenberId();
        BigDecimal Discount = new BigDecimal(1);
        if (memberId != null) Discount = menberMapper.selectMenberDiscountById(memberId);
        BigDecimal decimal = new BigDecimal(0);
        for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
            Integer dishId = entry.getKey();
            Integer dishNum = entry.getValue();
            Integer stock = dishMapper.selectDishNumberById(dishId);

            if (stock < dishNum) {
                String dishName = dishMapper.selectDishNameById(dishId);
                return R.error(dishName + "库存不足");
            }
            decimal = decimal.add(dishMapper.selectDishPriceById(dishId).multiply(BigDecimal.valueOf(dishNum)));
        }
        //保留两位小数
        decimal = decimal.multiply(Discount).multiply(order.getUserDist()).setScale(2, RoundingMode.HALF_UP);
        if (decimal.compareTo(order.getOrderMoney()) != 0) return R.error("订单金额错误");

        //删除库存
        for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
            Integer dishId = entry.getKey();
            Integer dishNum = entry.getValue();
            //更新库存
            dishMapper.updateDishNumberById(dishId, dishNum);
            //更新销量
            dishMapper.updateDishSalesById(dishId, dishNum);

        }
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        Order order1 = new Order();
        BeanUtils.copyProperties(order, order1);
        UserCoupon userCoupon = userCouponMapper.selectById(order.getCouponId());
        userCoupon.setStatus(1);
        userCouponMapper.updateById(userCoupon);
        orderMapper.insert(order1);// 调用save方法保存订单信息
        return R.ok();
    }

    @Override
    public R getUserOrder(QueryPageOrder order) {
        String sortField = order.getSortField();
        if (sortField != null && sortField.matches(".*[a-z]+[A-Z]+.*")) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < sortField.length(); i++) {
                char c = sortField.charAt(i);
                if (Character.isUpperCase(c)) sb.append("_").append(Character.toLowerCase(c));
                else sb.append(c);
            }
            order.setSortField(sb.toString());
        }
        Page<Order> mapPage = new Page<>(order.getCurrent(), order.getSize(), false);
        IPage<UserOrder> page = orderMapper.selectOrder(order, mapPage);
        List<UserOrder> records = page.getRecords();
        extracted(records);
        page.setRecords(records);
        page.setTotal(orderMapper.selectOrderCount(order));
        return R.ok().setData(page);
    }

    private static @NotNull Result getResult(Order order, BigDecimal bili) {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", order.getId());
        bizContent.put("refund_amount", order.getOrderMoney().multiply(bili).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
        bizContent.put("refund_reason", "用户取消订单");
        bizContent.put("out_request_no", UUID.randomUUID().toString().replace("-", ""));
        return new Result(alipayClient, bizContent);
    }

    @Override
    public R toPay(OrderPay orderPay) {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);

        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.return_url);
        alipayRequest.setNotifyUrl(AlipayConfig.notify_url);

        JSONObject bizContent = new JSONObject();
        //商户订单号，商家自定义，保持唯一性
        bizContent.put("out_trade_no", orderPay.getId());
        //支付金额，最小值0.01元
        bizContent.put("total_amount", orderPay.getOrderMoney());
        //订单标题，不可使用特殊符号
        bizContent.put("subject", orderPay.getRestaurantName());
        //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
        bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");

        alipayRequest.setBizContent(bizContent.toString());
        try {
            //请求
            String result = alipayClient.pageExecute(alipayRequest).getBody();
            System.out.println("*********************\n返回结果为：" + result);
            return R.ok().setData(result);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public R payNotify(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        Map<String, String[]> requestParams = request.getParameterMap();
        for (String name : requestParams.keySet()) {
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++)
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            params.put(name, valueStr);
        }
        try {
            // 验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);
            // 验证失败则记录异常日志，并返回failure。
            if (signVerified) {
                // 验证成功后，你需要做的事情，比如更新订单状态等
                String out_trade_no = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                String trade_no = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                String trade_status = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
                // 根据支付宝文档，TRADE_SUCCESS表示交易成功
                if (trade_status.equals("TRADE_SUCCESS")) {
                    // 更新订单状态，处理业务逻辑
                    Order order = new Order();
                    order.setId(Integer.valueOf(out_trade_no));
                    order.setTradeNo(trade_no);
                    order.setStatus(1);
                    order.setPaymentManner("支付宝");
                    order.setPaymentTime(new Date());
                    updateById(order);

                }
                webSocketServer.sendOneMessage("admin", "新订单");
                return R.ok();
            } else return R.error();
        } catch (Exception e) {
            // 处理异常
            return R.error();
        }

    }

    @Override
    public R refund(Integer orderId) {
        Order order = getById(orderId);
        if (order == null) return R.error("订单不存在");
        if (order.getStatus() == 0) return R.error("订单未支付");
        if (order.getStatus() == 2) return R.error("订单已取消");
        Date datePeriod = order.getDatePeriod();
        BigDecimal bili = new BigDecimal(1);
        //根据时间推算退款金额，小于10小时，退款金额百分之八十，小于8小时，退款金额百分之五十，小于6小时，退款金额百分之三十，小于4小时，不能退款
        long hours = (datePeriod.getTime() - System.currentTimeMillis()) / (60 * 60 * 1000);
        if (hours < 4) return R.error("订单时间距离当前时间小于4小时，无法取消");
        if (hours < 6) bili = new BigDecimal("0.3");
        else if (hours < 8) bili = new BigDecimal("0.5");
        else if (hours < 10) bili = new BigDecimal("0.8");
        Result result = OrderServiceImpl.getResult(order, bili);
        try {
            com.alipay.api.request.AlipayTradeRefundRequest request = new com.alipay.api.request.AlipayTradeRefundRequest();
            request.setBizContent(result.bizContent.toString());
            com.alipay.api.response.AlipayTradeRefundResponse response = result.alipayClient.execute(request);
            if (response.isSuccess()) return upDish(order);
            else return R.error();
        } catch (Exception e) {
            return R.error();
        }
    }

    @NotNull
    private R upDish(Order order) throws IOException {
        String orderDetail = order.getOrderDetail();
        Map<Integer, Integer> orderDetails = objectMapper.readValue(orderDetail, new TypeReference<Map<Integer, Integer>>() {
        });
        for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
            Integer dishId = entry.getKey();
            Integer dishNum = entry.getValue();
            dishMapper.updateDishNumberById(dishId, -dishNum);
        }
        order.setStatus(4);
        updateById(order);
        return R.ok();
    }

    private void extracted(List<UserOrder> records) {
        for (UserOrder userOrder : records) {
            String orderDetail = userOrder.getOrderDetail();
            try {
                Map<Integer, Integer> orderDetails = objectMapper.readValue(orderDetail, new TypeReference<Map<Integer, Integer>>() {
                });
                List<UserOrder.Dish> dishList = new ArrayList<>();
                for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
                    UserOrder.Dish dish = new UserOrder.Dish();
                    dish.setDishName(dishMapper.selectDishNameById(entry.getKey()));
                    dish.setDishNum(entry.getValue());
                    dishList.add(dish);
                }
                userOrder.setDishNameList(dishList);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public R cancelOrder(Integer orderId) throws IOException {
        Order order = getById(orderId);
        if (order == null) return R.error("订单不存在");
        if (order.getStatus() == 2) return R.error("订单已取消");
        return upDish(order);
    }

    @Override
    public R getOrderByRestaurantId(QueryPageOrderMerchant orderMerchant) {
        Integer masterId = restaurantMapper.selectMasterIdById(orderMerchant.getRestaurantId());
        if (masterId == null) return R.error("餐厅不存在");
        if (!Objects.equals(masterId, orderMerchant.getMasterId())) return R.error("餐厅信息错误");

        String sortField = orderMerchant.getSortField();
        //如果排序订单是小写➕大写，转换为小写➕下划线
        if (sortField != null && sortField.matches(".*[a-z]+[A-Z]+.*")) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < sortField.length(); i++) {
                char c = sortField.charAt(i);
                if (Character.isUpperCase(c)) sb.append("_").append(Character.toLowerCase(c));
                else sb.append(c);
            }
            orderMerchant.setSortField(sb.toString());
        }

        Page<Order> mapPage = new Page<>(orderMerchant.getCurrent(), orderMerchant.getSize(), false);
        IPage<MerchantOrders> page = orderMapper.selectOrderByMerchant(orderMerchant, mapPage);
        List<MerchantOrders> records = page.getRecords();

        for (MerchantOrders merchantOrders : records) {
            String orderDetail = merchantOrders.getOrderDetail();
            try {
                Map<Integer, Integer> orderDetails = objectMapper.readValue(orderDetail, new TypeReference<Map<Integer, Integer>>() {
                });
                List<UserOrder.Dish> dishList = new ArrayList<>();
                for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
                    UserOrder.Dish dish = new UserOrder.Dish();
                    dish.setDishName(dishMapper.selectDishNameById(entry.getKey()));
                    dish.setDishNum(entry.getValue());
                    dishList.add(dish);
                }
                merchantOrders.setDishNameList(dishList);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        page.setRecords(records);
        page.setTotal(orderMapper.selectOrderCountByMerchant(orderMerchant));
        return R.ok().setData(page);
    }

    @Override
    public R score(ScoreDao scoreDao) {
        Order order = orderMapper.selectById(scoreDao.getOrderId());
        if (order.getStatus() == 3) return R.error("订单已评价");
        Integer count = orderMapper.selectOrderBy(scoreDao.getOrderId(), scoreDao.getUserId(), scoreDao.getRestaurantId());
        if (count == 0) return R.error("订单信息错误");
        restaurantMapper.updateRestaurantScore(scoreDao.getRestaurantId(), scoreDao.getRestaurantScore());

        List<ScoreDao.Dish> dishList = scoreDao.getDishList();
        for (ScoreDao.Dish dish : dishList) {
            Integer dishId = dish.getDishId();
            BigDecimal dishScore = dish.getDishScore();
            dishMapper.updateDishScoreById(dishId, dishScore);
        }
        order.setStatus(3);
        updateById(order);
        return R.ok();
    }

    @Override
    public R getOrderById(Integer orderId) {
        Order order = getById(orderId);
        if (order == null) return R.error("订单不存在");

        var dishIdAndNames = new ArrayList<DishIdAndName>();
        try {
            Map<Integer, Integer> orderDetails = objectMapper.readValue(order.getOrderDetail(), new TypeReference<Map<Integer, Integer>>() {
            });
            for (Map.Entry<Integer, Integer> entry : orderDetails.entrySet()) {
                var dishIdAndName = new DishIdAndName();
                dishIdAndName.setId(entry.getKey());
                dishIdAndName.setName(dishMapper.selectDishNameById(entry.getKey()));
                dishIdAndNames.add(dishIdAndName);
            }
        } catch (IOException e) {
            return R.error("Failed to parse order details");
        }
        return R.ok().setData(dishIdAndNames);
    }

    private static class Result {
        public final AlipayClient alipayClient;
        public final JSONObject bizContent;

        public Result(AlipayClient alipayClient, JSONObject bizContent) {
            this.alipayClient = alipayClient;
            this.bizContent = bizContent;
        }
    }

    @Override
    public R getTodayOrder(Integer restaurantId) {
        LocalDateTime now = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime tomorrow = now.plusDays(1);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String nowString = now.format(formatter);
        String tomorrowString = tomorrow.format(formatter);
        List<MerchantOrders> todayOrder = orderMapper.selectTodayOrder(restaurantId, nowString, tomorrowString);
        return R.ok().setData(todayOrder);

    }

    @Override
    public R getRestaurantOrder(Integer userId) {
        List<RestaurantOrder> orderCounts = orderMapper.selectOrderCountByRestaurantAndUser(userId);
        if (orderCounts.isEmpty()) return R.error("没有数据");
        for (RestaurantOrder orderCount : orderCounts)
            orderCount.setRestaurantName(restaurantMapper.selectRestaurantNameByid(orderCount.getRestaurantId()));
        return R.ok().setData(orderCounts);
    }

    @Override
    public R test1(Integer orderId) {
        Order order = getById(orderId);
        order.setStatus(2);
        updateById(order);
        return R.ok();
    }

    @Override
    public R test2(Integer orderId) {
        Order order = getById(orderId);
        order.setStatus(3);
        updateById(order);
        return R.ok();
    }
}



